#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ipc.h>
#include <sys/shm.h> 
#include <sys/sem.h>

#include "quadrdefs.h"
#include "fns.h"


/* This will check if the given function is available*/
/*int isFunAvailable (char *strPtr){
  int i =0;
  for (i = 0; i < nFns; i++){
  if (strcmp (strPtr, fns[i].name) == 0)
  return i;
  }
  return -1;
  }
 */

/* Pass 0/1 as 2nd argument to checkNum funtion to check if it is double or not */
int checkNum (char *strPtr, int isDouble){
	int i = 0;
	int flag = 0;

	if (strPtr[0] == '.'){
		return 1;
	}

	for (i = 0; i < strlen (strPtr); i++){
		if (strPtr[i] == '.'){
			if (flag == 0)
				flag = 1;
			else
				return 1;
		}
	}

	for (i = 0; i < strlen (strPtr); i ++){
		if (isDouble == 1){

			if (!isdigit((int)strPtr[i]) && strPtr[i] != 'e' && strPtr[i] != 'E' && strPtr[i] != '+' && strPtr[i] != '.' &&
					strPtr[i] != '-'){
				return 1;
			}
		}
		else{
			if (!isdigit((int)strPtr[i])){
				return 1;
			}

		}
		/* Check for same operator does not occur. eg. 12++1, 12-+1, etc */
		if (strPtr[i] == '+' || strPtr[i] == '.' || strPtr[i] == '-'){
			if (!isdigit ((int)strPtr[i+1] )) {
				return 1;
			}
			if ( i != 0 && strPtr[i - 1] == ' ' && strPtr[i-1] == 'e'){
				return 1;
			}
		}
		if (strPtr[i] == 'e' || strPtr[i] == 'E'){
			if (!isdigit((int)strPtr[i+1]) && strlen (strPtr) <= i + 1){
				return 1;
			}
		}
	}
	return 0;
}


Params validate_input(char *line){

	Params prm;

	char *strPtr = NULL, *pEnd = NULL;
	int i = 0, flag = 1;

	strPtr = strtok (line, " \n");

	if(strcmp(strPtr, "quit") == 0){
		prm.error = 1;
		return prm;
	}

	while (strPtr != NULL){

		if ( i == 0){
			//if ( getFnForString (strPtr)){
			prm.fnName = (char *)malloc (sizeof (char) * strlen (strPtr));
			strcpy (prm.fnName, strPtr);
			//	}
			//else{
			fprintf (stderr, "\nERROR: Function \"%s\" is not defined\n", strPtr);
			flag = 1;
			prm.error = -1;
			return prm;
			//}
		}


		if ( i == 1){
			/* Pass 1 as 2nd argument to checkNum funtion if the entered value is double */
			if (checkNum(strPtr, 1) == 0){
				prm.lo_bound = strtod (strPtr, &pEnd);
			}
			else{
				fprintf (stderr, "\nERROR: Invalid LO_BOUND value : %s\n", strPtr);
				flag = 1;
				prm.error = -1;
				return prm;
			}
		}

		if ( i == 2){
			if (checkNum (strPtr, 1) == 0){
				prm.hi_bound = strtod (strPtr, &pEnd);
			}
			else{
				fprintf (stderr, "\nERROR: Invalid HI_BOUND value : %s\n", strPtr);
				flag = 1;
				prm.error = -1;
				return prm;
			}
		}

		if ( i == 3){
			/* Pass 0 as 2nd argument to checkNum funtion if the entered value is not double */
			if (checkNum (strPtr, 0) == 0){
				prm.n_intervals = atoi (strPtr);
			}
			else{
				fprintf (stderr, "\nERROR: Invalid N_INTERVALS : %s\n", strPtr);
				flag = 1;
				prm.error = -1;
				return prm;
			}
		}

		if ( i >= 4){
			fprintf (stderr, "\nERROR: Illegal input %s\n", strPtr);
			flag = 1;
			prm.error = -1;
			return prm;
		}

		i++;
		/* Read next argument from line by using strtok */
		strPtr = strtok (NULL, " \n");
	}

	i--;
	if (i != 3){
		fprintf (stderr, "\nERROR: Insufficient values provided\n");
		prm.error = -1;
		return prm;
	}

	return prm;

}

int main (int argc, char *argv[]){

	char line[BUFSIZE] = "", *buf;
	key_t key;
	int shmid, readCnt, semid;
	struct sembuf sops[3];
	//Params prm;


	/*line = (char *)malloc (sizeof (char));
	  if (!line){
	  fprintf (stderr, "Not enough memory\n");
	  exit(1);
	  }*/


	if (argc != 2) {
		fprintf(stderr, "usage: %s DIR\n", argv[0]); exit(1);
	}

	//printf ("Dir : %s\n",argv[1]);


	fprintf (stdout, "Daemon PID : %ld\n",(long)getpid());

	key = ftok("fns.h",0);
	if (key == (key_t) - 1){
		fprintf (stderr, "ERROR: ftok failed\n");
		exit(1);
	}

	//chdir (argv[1]);
	/* get a semaphore set identifier */
	semid = semget( key, 3, 0666 );
	if ( semid == -1 )
	{
		fprintf(stderr,"ERROR: semget() failed\n");
		exit(-1);
	}

	if ((shmid = shmget(key, MAXSZ, 0666)) < 0){
		fprintf (stderr, "ERROR: Unable to create shared memory\n");
		exit(1);
	}

	if ((buf = shmat (shmid, NULL, 0666)) < 0){
		fprintf (stderr, "ERROR: Unable to attach shared memory\n");
		exit(1);
	}

	while (1){
		line[0] = '\0';
		fprintf (stdout, "\nFN LO_BOUND HI_BOUND N_INTERVALS\n");
		fprintf (stdout, "(Type 'quit' for closing.)\n");

		if ((readCnt = read (STDIN_FILENO, line, BUFSIZE)) == -1){
			fprintf (stderr, "ERROR: Unable to read line\n");
			exit(1);
		}
		line [readCnt + 1] = '\0';

		/*prm = validate_input (line);
		  if (prm.error == -1){
		  fprintf (stderr, "Invalid Input\n");
		  continue;
		  }
		  line [0] = '\0';
		  printf ("%s %g %g %d\n",prm.fnName, prm.lo_bound, prm.hi_bound, prm.n_intervals);

		  sprintf (line, "%s %g %g %d\n",prm.fnName, prm.lo_bound, prm.hi_bound, prm.n_intervals);
		 */

		if ( strncmp (line, "quit",4) == 0)
			break;

		/* Take shared mem lock */
		sops[0].sem_num = 1;
		sops[0].sem_op = 0;
		sops[0].sem_flg = SEM_UNDO;

		sops[1].sem_num = 1;
		sops[1].sem_op = 1;
		sops[1].sem_flg = SEM_UNDO;

		if (semop (semid, sops, 2) == -1){
			fprintf (stderr, "ERROR: semop(): shared memory op failed \n");
			exit(1);
		}

		/*Copy request to shared memory */
		strcpy (buf, line);
		buf [strlen(line) + 1] = '\0';


		/* Send server indication that request is written */
		sops[0].sem_num = 0;
		sops[0].sem_op = 1;
		sops[0].sem_flg = SEM_UNDO;

		if (semop (semid, sops, 1) < 0){
			fprintf (stderr, "ERROR: semop(): notification op failed \n");
			exit(1);
		}

		sops[0].sem_num = 2;
		sops[0].sem_op = -1;
		sops[0].sem_flg = SEM_UNDO;

		if (semop (semid, sops, 1) < 0){
			fprintf (stderr, "ERROR: semop(): wait for server's result op failed \n");
			exit(1);
		}

		if (strncmp (buf, "Invalid Arguments",17) != 0){

			fprintf(stdout,"Client Result : %s\n",buf);
			buf[0] = '\0';
		}
		else{
			fprintf (stdout,"%s\n",buf);
			buf[0] = '\0';
		}

		/*Release shared memory */
		sops[0].sem_num = 1;
		sops[0].sem_op = -1;
		sops[0].sem_flg = SEM_UNDO;

		if ((semop (semid, sops, 1)) < 0){
			fprintf (stderr, "ERROR: semop(): release shared memory op failed \n");
			perror("semop() for shared mem failed ");
			exit(1);
		}
	}
	if (shmdt(buf) == -1)
	{
		fprintf(stderr, "ERROR: shmdt() failed\n");
		exit(1);
	}

	return 0;

}
